Шаг. 1 Загрузка данных и необходимых библиотек.
Шаг. 2 Знакомство и анализ данных:
str;Шаг. 3 Предобработка данных:
Шаг. 4 Получить основные статистические характеристики данных, проанализировать портреты клиентов и вычислить корреляцию. Визуализировать результаты, сделать выводы.
Шаг. 5 Определить статистическую значимость между определенными группами клиентов, а также между различными признаками, провести тесты, сделать выводы.
Шаг. 6 На основе полученных ранее результатах сделать выводы об основных признаков, которые влияют на отток клиентов. При необходимости провести новые тесты, определить интервалы признаков, написать полученные выводы.
Шаг. 7 Определить высокоотточные сегменты, и притезировать их по различным метрикам (выявить наиболее валидную метрику).
Шаг. 8 Детальнее проанализировать полученные сегменты, постараться эти сегменты описать, и на фоне полученного описания предложить рекомендации для получения больших денег для бизнеса (например, для определенного сегмента придумать акцию или проект по его удержанию).
Шаг. 9 Подготовить презентацию, куда добавить основные полученные графики и результаты, а также коротко описать результаты и сформулированные выводы, а также добавить рекомендации.
Шаг. 10 Подготовить речь для презентации и сформулировать общие выводы.
Датасет содержит данные о клиентах банка «Метанпром». Банк располагается в Ярославле и областных городах: Ростов Великий и Рыбинск.
Колонки:
user_id — идентификатор пользователя;score — баллы кредитного скоринга;city — город;gender — пол;age — возраст;equity — количество баллов собственности;Balance — баланс на счёте;Products — количество продуктов, которыми пользуется клиент;CreditCard — есть ли кредитная карта;last_activity — активный клиент;estimated_salary — заработная плата клиента;Churn — ушёл или нет.Импортируем необходимые библиотеки для работы:
import pandas as pd
import numpy as np
import seaborn as sns
import phik
from phik import resources, report
import matplotlib.pyplot as plt
import scipy.stats as stats
import plotly.express as px
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from scipy import stats as st
Прочтем файл bank_scrooge.csv с данными и сохранение его в переменную bank_scrooge:
path = "https://drive.google.com/uc?export=download&id=1-U61mhTz_N1ARjy2XSAZ7IlQqGjeqP0F"
bank_scrooge = pd.read_csv(path)
Выведем первые 5 строк датасета, ознакомимся с данными, методом info(), посмотрим на количество пропущенных значений в датасете, проверим на явные дубликаты:
def df_info(df):
display(df.head())
display(df.info())
display(df.isna().sum())
print('Количество явных дубликатов в датасете data:', df.duplicated().sum())
df_info(bank_scrooge)
| USERID | score | city | gender | age | equity | balance | products | credit_card | last_activity | EST_SALARY | churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 183012 | 850.0 | Рыбинск | Ж | 25.0 | 1 | 59214.82 | 2 | 0 | 1 | 75719.14 | 1 |
| 1 | 146556 | 861.0 | Рыбинск | Ж | 37.0 | 5 | 850594.33 | 3 | 1 | 0 | 86621.77 | 0 |
| 2 | 120722 | 892.0 | Рыбинск | Ж | 30.0 | 0 | NaN | 1 | 1 | 1 | 107683.34 | 0 |
| 3 | 225363 | 866.0 | Ярославль | Ж | 51.0 | 5 | 1524746.26 | 2 | 0 | 1 | 174423.53 | 1 |
| 4 | 157978 | 730.0 | Ярославль | М | 34.0 | 5 | 174.00 | 1 | 1 | 0 | 67353.16 | 1 |
<class 'pandas.core.frame.DataFrame'> RangeIndex: 10000 entries, 0 to 9999 Data columns (total 12 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 USERID 10000 non-null int64 1 score 10000 non-null float64 2 city 10000 non-null object 3 gender 10000 non-null object 4 age 9974 non-null float64 5 equity 10000 non-null int64 6 balance 7705 non-null float64 7 products 10000 non-null int64 8 credit_card 10000 non-null int64 9 last_activity 10000 non-null int64 10 EST_SALARY 10000 non-null float64 11 churn 10000 non-null int64 dtypes: float64(4), int64(6), object(2) memory usage: 937.6+ KB
None
USERID 0 score 0 city 0 gender 0 age 26 equity 0 balance 2295 products 0 credit_card 0 last_activity 0 EST_SALARY 0 churn 0 dtype: int64
Количество явных дубликатов в датасете data: 0
Проверим на неявные дубликаты, для этого выведем уникальные значения каждого столбца:
def checking_implicit_duplicates(df, column):
print(f"Уникальные значения стобца {column}:")
print(df[column].unique())
for column in bank_scrooge.columns:
checking_implicit_duplicates(bank_scrooge, column)
print()
Уникальные значения стобца USERID: [183012 146556 120722 ... 115639 148700 96267] Уникальные значения стобца score: [ 850. 861. 892. 866. 730. 856. 807. 825. 923. 906. 927. 921. 845. 837. 815. 731. 829. 793. 876. 922. 772. 896. 871. 732. 898. 806. 766. 776. 868. 946. 828. 786. 944. 863. 822. 794. 924. 811. 814. 956. 935. 826. 849. 901. 893. 792. 888. 831. 748. 820. 860. 914. 930. 948. 789. 737. 812. 933. 809. 749. 873. 859. 950. 937. 835. 768. 767. 785. 846. 750. 816. 894. 891. 788. 758. 787. 821. 855. 779. 791. 838. 971. 797. 841. 915. 985. 870. 897. 763. 774. 686. 1000. 875. 819. 869. 854. 810. 761. 802. 958. 917. 745. 736. 908. 928. 884. 853. 780. 726. 881. 975. 852. 965. 899. 890. 836. 818. 880. 795. 916. 775. 902. 918. 842. 771. 895. 801. 721. 966. 804. 885. 986. 753. 832. 979. 798. 754. 756. 823. 658. 734. 954. 827. 929. 760. 688. 659. 817. 718. 705. 920. 652. 649. 862. 770. 824. 934. 952. 977. 840. 709. 878. 808. 900. 778. 800. 813. 905. 925. 697. 942. 848. 717. 712. 973. 879. 987. 805. 872. 904. 939. 993. 790. 799. 943. 903. 839. 882. 765. 883. 964. 833. 739. 911. 926. 751. 803. 762. 907. 910. 694. 867. 877. 706. 708. 691. 742. 889. 919. 864. 663. 913. 940. 887. 953. 746. 699. 858. 834. 847. 727. 782. 720. 938. 711. 725. 912. 851. 743. 685. 830. 701. 733. 777. 784. 949. 724. 769. 687. 773. 857. 963. 741. 931. 974. 764. 886. 844. 874. 909. 951. 947. 689. 715. 865. 945. 796. 682. 714. 976. 992. 707. 713. 781. 668. 843. 932. 752. 962. 759. 757. 967. 646. 738. 722. 936. 664. 661. 728. 997. 755. 673. 703. 941. 988. 695. 961. 960. 651. 696. 653. 983. 972. 716. 692. 959. 783. 735. 968. 702. 740. 747. 955. 970. 990. 681. 744. 729. 982. 969. 980. 978. 710. 719. 655. 650. 991. 642. 700. 981. 657. 996. 669. 999. 698. 989. 957. 666. 671. 678. 693. 648. 690. 684. 994. 984. 723. 667. 654. 680. 998. 660. 670. 656. 665. 662. 704. 995.] Уникальные значения стобца city: ['Рыбинск' 'Ярославль' 'Ростов'] Уникальные значения стобца gender: ['Ж' 'М'] Уникальные значения стобца age: [25. 37. 30. 51. 34. 56. 39. 38. 54. 67. 52. 41. 31. 28. 35. 42. 45. 53. 40. 43. 47. 48. 61. 62. 36. 26. 27. 60. 72. 50. 46. 55. 58. 71. 44. 49. 33. 32. 66. 29. 68. 73. 69. 59. 21. 65. 57. 63. 80. 24. 70. 77. 64. 23. 20. 18. 19. 78. 74. 76. 22. 75. nan 83. 82. 81. 84. 79. 86.] Уникальные значения стобца equity: [1 5 0 4 3 2 6 7 8 9] Уникальные значения стобца balance: [ 59214.82 850594.33 nan ... 78144.65 865457.97 1180886.7 ] Уникальные значения стобца products: [2 3 1 4 5 0] Уникальные значения стобца credit_card: [0 1] Уникальные значения стобца last_activity: [1 0] Уникальные значения стобца EST_SALARY: [ 75719.14 86621.77 107683.34 ... 108905.09 86874.9 21422.37] Уникальные значения стобца churn: [1 0]
Построим распределение данных по данным столбцов:
bank_scrooge.hist(figsize = (40, 30));
Вывод: походу выполнения раздела "Обзор данных" были импортированы необходимые библиотеки для работы и визуализации данных. Был загружен датасет в переменную bank_scrooge. Также были выведены первые 5 строк датасета на экран, для ознакомления с данными. Были построены гистограммы для данных столбцов датасета, чтобы ознакомиться с распределениями данных. Аномалий в данных не было обнаружено.
Были обнаружены пропуски в данных, а именно в столбцах: age и balance, их количество равно 26 и 2295 соответственно.
Также необходимо привести все названия столбцов к "змеиному виду", т.е. заглавные буквы заменить на строчные, а также разделить слова нижним знаком подчеркивания. Возможно, стоит заменить тип данных столбца score с типа данных float на int, так как на первый взгляд там только целые числа. Столбец данных age (возраст) необходимо перевести в тип данных int, так как возраст не может быть дробный. Для удобства можно также перевести столбцы balance (баланс на счете) и estimated_salary (заработная плата клиента) в тип данных int, так нам не смысла знать сумму до копеек, а это округление не сыграет на точности.
Явные и неявные дубликаты в данных не были обнаружены.
Также есть смысл заменить численные значения (0 или 1) в столбцах last_activity (активный клиент), credit_card (есть ли кредитная карта), churn (ушёл или нет) на категориальные значения, например, на слова да или нет.
Приведение столбцов к нижнему регистру и добавление нижнего знака подчеркивания между словами:
bank_scrooge.columns = bank_scrooge.columns.str.lower()
bank_scrooge = bank_scrooge.rename(columns = {'userid': 'user_id', 'creditcard': 'credit_card', 'est_salary': 'estimated_salary'})
Работа с пропусками в данных:
Посмотрим на столбец age в котором 26 пропусков:
bank_scrooge['age'].value_counts()
36.0 414
37.0 402
33.0 392
35.0 388
34.0 384
...
83.0 3
82.0 2
84.0 2
79.0 2
86.0 2
Name: age, Length: 68, dtype: int64
Вычислим отношение пропущенных значений в столбце ко всему количеству данных:
print(f"Доля пропущенных значений ко всему количеству равно: {bank_scrooge['age'].isna().sum() / 10000}")
Доля пропущенных значений ко всему количеству равно: 0.0026
Посмотрим на строки, где есть пропущенные значения в столбце age:
bank_scrooge[bank_scrooge['age'].isnull()]
| user_id | score | city | gender | age | equity | balance | products | credit_card | last_activity | estimated_salary | churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1247 | 228075 | 932.0 | Ярославль | М | NaN | 5 | 7601719.20 | 2 | 1 | 1 | 408121.16 | 0 |
| 2165 | 187635 | 692.0 | Рыбинск | Ж | NaN | 0 | NaN | 1 | 1 | 1 | 160368.82 | 0 |
| 2444 | 221156 | 913.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 1 | 135693.24 | 0 |
| 3091 | 138660 | 836.0 | Ростов | Ж | NaN | 5 | 294315.53 | 2 | 0 | 1 | 63310.22 | 1 |
| 4912 | 210674 | 834.0 | Рыбинск | М | NaN | 1 | 238330.52 | 2 | 0 | 1 | 93775.06 | 0 |
| 5470 | 218868 | 827.0 | Рыбинск | Ж | NaN | 4 | 448959.07 | 2 | 1 | 1 | 67835.95 | 0 |
| 5495 | 151662 | 884.0 | Рыбинск | Ж | NaN | 0 | NaN | 1 | 1 | 1 | 137500.77 | 0 |
| 7236 | 210135 | 908.0 | Рыбинск | Ж | NaN | 4 | 1120340.31 | 3 | 1 | 1 | 85002.15 | 0 |
| 7248 | 219343 | 920.0 | Рыбинск | Ж | NaN | 0 | NaN | 1 | 1 | 0 | 159248.67 | 0 |
| 7345 | 184913 | 829.0 | Ярославль | Ж | NaN | 3 | 188648.77 | 2 | 0 | 1 | 75206.90 | 0 |
| 7409 | 214031 | 777.0 | Ярославль | М | NaN | 2 | 171510.23 | 1 | 1 | 1 | 75409.63 | 0 |
| 8015 | 198635 | 670.0 | Ярославль | Ж | NaN | 0 | NaN | 1 | 1 | 1 | 168699.33 | 0 |
| 8070 | 226550 | 940.0 | Рыбинск | М | NaN | 0 | NaN | 1 | 0 | 1 | 147696.95 | 0 |
| 8293 | 216848 | 930.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 1 | 199542.51 | 0 |
| 8385 | 206759 | 915.0 | Рыбинск | М | NaN | 0 | NaN | 1 | 1 | 0 | 71179.53 | 0 |
| 8449 | 210898 | 805.0 | Ярославль | Ж | NaN | 0 | NaN | 1 | 0 | 1 | 922080.25 | 0 |
| 8632 | 221197 | 893.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 0 | 173929.92 | 0 |
| 8785 | 127440 | 663.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 1 | 117197.56 | 0 |
| 9104 | 222480 | 776.0 | Рыбинск | Ж | NaN | 5 | 796735.09 | 1 | 1 | 1 | 55073.63 | 0 |
| 9301 | 202983 | 942.0 | Рыбинск | Ж | NaN | 0 | NaN | 1 | 1 | 1 | 163804.73 | 0 |
| 9380 | 187459 | 894.0 | Рыбинск | М | NaN | 0 | NaN | 1 | 1 | 0 | 178012.28 | 0 |
| 9457 | 141945 | 929.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 0 | 381868.89 | 0 |
| 9632 | 185829 | 927.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 0 | 231254.86 | 0 |
| 9634 | 221809 | 917.0 | Ярославль | М | NaN | 0 | NaN | 1 | 1 | 1 | 192644.15 | 0 |
| 9667 | 163657 | 849.0 | Ярославль | М | NaN | 4 | 1254013.85 | 2 | 1 | 1 | 119106.67 | 0 |
| 9819 | 140934 | 832.0 | Рыбинск | Ж | NaN | 3 | 385763.16 | 2 | 0 | 1 | 59651.35 | 0 |
Как можно увидеть, что пропусков в столбце age не так много, всего 26 или 0.26% от всего количества. А также большинство строк, в которых есть пропуск по возрасту, также имеется пропуски и в столбце balance (баланс на счёте). Но также есть среди полученных данных и довольно богатые клиенты, например, под номером user_id 228075, у которого более 7 млн. рублей на балансе. И нет возможности заменить пропуски каким-либо релевантным значением, например, медианным значением по категориям. Так что в данном случае, лучше пропуски удалить, для того чтобы в будущем работать с данным столбцом:
bank_scrooge = bank_scrooge.dropna(subset = ['age'])
Посмотрим на столбец balance в котором было 2295 пропусков:
bank_scrooge['balance']
0 59214.82
1 850594.33
2 NaN
3 1524746.26
4 174.00
...
9995 78144.65
9996 NaN
9997 NaN
9998 865457.97
9999 1180886.70
Name: balance, Length: 9974, dtype: float64
Вычислим отношение пропущенных значений в столбце ко всему количеству данных:
print(f"Доля пропущенных значений ко всему количеству равно: {bank_scrooge['balance'].isna().sum() / 10000}")
Доля пропущенных значений ко всему количеству равно: 0.2279
Доля пропущенных значений в столбце balance довольно высока, она равна 0.2279 или 23%. Так что пропущенные значения точно не стоит удалять, лучше попробовать выяснить природу этих пропусков. Для этого посмотрим на строки, где есть пропущенные значения в столбце balance:
bank_scrooge[bank_scrooge['balance'].isnull()]
| user_id | score | city | gender | age | equity | balance | products | credit_card | last_activity | estimated_salary | churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | 120722 | 892.0 | Рыбинск | Ж | 30.0 | 0 | NaN | 1 | 1 | 1 | 107683.34 | 0 |
| 9 | 133130 | 906.0 | Ярославль | Ж | 67.0 | 0 | NaN | 1 | 0 | 1 | 238055.53 | 0 |
| 10 | 148929 | 927.0 | Ростов | М | 52.0 | 0 | NaN | 1 | 1 | 1 | 196820.07 | 0 |
| 11 | 172184 | 921.0 | Ростов | М | 41.0 | 0 | NaN | 1 | 1 | 1 | 217469.48 | 0 |
| 19 | 127034 | 922.0 | Рыбинск | Ж | 53.0 | 0 | NaN | 1 | 0 | 0 | 147094.82 | 0 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 9976 | 208085 | 876.0 | Ростов | М | 38.0 | 0 | NaN | 1 | 0 | 0 | 171763.69 | 0 |
| 9984 | 125941 | 729.0 | Ярославль | Ж | 42.0 | 0 | NaN | 1 | 1 | 1 | 687538.70 | 0 |
| 9993 | 219924 | 884.0 | Рыбинск | Ж | 36.0 | 0 | NaN | 1 | 1 | 1 | 169844.88 | 0 |
| 9996 | 139170 | 894.0 | Ярославль | М | 46.0 | 0 | NaN | 1 | 1 | 0 | 196898.29 | 0 |
| 9997 | 115639 | 903.0 | Ярославль | М | 24.0 | 0 | NaN | 2 | 1 | 1 | 108905.09 | 0 |
2279 rows × 12 columns
Исходя из полученных результатов, очень сложно сделать вывод, о том какая причина стоит в пропусках данных, может быть это дебетовый счет в банке. Или же ошибка на сервере, но это маловероятно, так как такое большое количество пропусков только в данном столбце. В данном случае лучше всего эти пропуски оставить как есть.
Изменение типов данных:
Посмотрим на столбец score и выясним можно ли его привести у типу данных int:
bank_scrooge['score']
0 850.0
1 861.0
2 892.0
3 866.0
4 730.0
...
9995 814.0
9996 894.0
9997 903.0
9998 777.0
9999 828.0
Name: score, Length: 9974, dtype: float64
bank_scrooge['score'].value_counts()
900.0 80
889.0 75
899.0 70
894.0 69
912.0 68
..
654.0 1
671.0 1
673.0 1
684.0 1
995.0 1
Name: score, Length: 348, dtype: int64
Переведем столбец score в тип данных int:
bank_scrooge['score'] = bank_scrooge['score'].astype('int')
Переведем столбцы age, balance и estimated_salary в тип данных int:
bank_scrooge['estimated_salary'] = bank_scrooge['estimated_salary'].astype('int')
Замена данных:
bank_scrooge['credit_card'].value_counts()
1 6784 0 3190 Name: credit_card, dtype: int64
Приведение столбца gender к числовому виду, чтобы в дальнейшем было удобно работать с этими столбцом:
bank_scrooge['gender'] = bank_scrooge['gender'].str.replace("М", '1')
bank_scrooge['gender'] = bank_scrooge['gender'].str.replace("Ж", '0')
bank_scrooge['gender'] = bank_scrooge['gender'].astype('int')
В итоге получается в столбце gender 1 будет значить мужской пол, а 0 - женский пол.
Получим описательную статистику для данных:
bank_scrooge.drop(columns = ['user_id', 'city', 'gender'], axis = 1).describe()
| score | age | equity | balance | products | credit_card | last_activity | estimated_salary | churn | |
|---|---|---|---|---|---|---|---|---|---|
| count | 9974.000000 | 9974.000000 | 9974.000000 | 7.695000e+03 | 9974.000000 | 9974.000000 | 9974.000000 | 9.974000e+03 | 9974.000000 |
| mean | 848.682575 | 42.734409 | 2.630840 | 8.272456e+05 | 1.871466 | 0.680168 | 0.522859 | 1.477863e+05 | 0.182374 |
| std | 65.399720 | 12.179971 | 1.979965 | 1.980327e+06 | 0.792729 | 0.466435 | 0.499502 | 1.392855e+05 | 0.386172 |
| min | 642.000000 | 18.000000 | 0.000000 | 0.000000e+00 | 0.000000 | 0.000000 | 0.000000 | 2.546000e+03 | 0.000000 |
| 25% | 802.000000 | 33.000000 | 0.000000 | 2.956987e+05 | 1.000000 | 0.000000 | 0.000000 | 7.525125e+04 | 0.000000 |
| 50% | 853.000000 | 40.000000 | 3.000000 | 5.242953e+05 | 2.000000 | 1.000000 | 1.000000 | 1.196265e+05 | 0.000000 |
| 75% | 900.000000 | 51.000000 | 4.000000 | 9.800512e+05 | 2.000000 | 1.000000 | 1.000000 | 1.744990e+05 | 0.000000 |
| max | 1000.000000 | 86.000000 | 9.000000 | 1.191136e+08 | 5.000000 | 1.000000 | 1.000000 | 1.395064e+06 | 1.000000 |
Проведем корриляционный анализ:
bank_scrooge_corr = bank_scrooge.drop(columns = ['user_id'], axis = 1).phik_matrix(verbose = False)
display(bank_scrooge_corr)
| score | city | gender | age | equity | balance | products | credit_card | last_activity | estimated_salary | churn | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| score | 1.000000 | 0.090925 | 0.070446 | 0.036178 | 0.527334 | 0.170887 | 0.307399 | 0.208318 | 0.053418 | 0.395214 | 0.228080 |
| city | 0.090925 | 1.000000 | 0.011924 | 0.093705 | 0.062559 | 0.039931 | 0.223196 | 0.074660 | 0.030399 | 0.170948 | 0.016431 |
| gender | 0.070446 | 0.011924 | 1.000000 | 0.286026 | 0.063699 | 0.045434 | 0.122023 | 0.195831 | 0.012733 | 0.144090 | 0.219731 |
| age | 0.036178 | 0.093705 | 0.286026 | 1.000000 | 0.043708 | 0.024989 | 0.133418 | 0.157196 | 0.089172 | 0.358167 | 0.182538 |
| equity | 0.527334 | 0.062559 | 0.063699 | 0.043708 | 1.000000 | 0.000000 | 0.441413 | 0.221467 | 0.051218 | 0.285543 | 0.352103 |
| balance | 0.170887 | 0.039931 | 0.045434 | 0.024989 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.374366 | 0.024672 |
| products | 0.307399 | 0.223196 | 0.122023 | 0.133418 | 0.441413 | 0.000000 | 1.000000 | 0.459117 | 0.191921 | 0.157477 | 0.440923 |
| credit_card | 0.208318 | 0.074660 | 0.195831 | 0.157196 | 0.221467 | 0.000000 | 0.459117 | 1.000000 | 0.049306 | 0.038496 | 0.202374 |
| last_activity | 0.053418 | 0.030399 | 0.012733 | 0.089172 | 0.051218 | 0.000000 | 0.191921 | 0.049306 | 1.000000 | 0.044890 | 0.262164 |
| estimated_salary | 0.395214 | 0.170948 | 0.144090 | 0.358167 | 0.285543 | 0.374366 | 0.157477 | 0.038496 | 0.044890 | 1.000000 | 0.048803 |
| churn | 0.228080 | 0.016431 | 0.219731 | 0.182538 | 0.352103 | 0.024672 | 0.440923 | 0.202374 | 0.262164 | 0.048803 | 1.000000 |
Можно увидеть, что есть прямая зависимость между столбцом churn и столбцами: equity (количество баллов собственности), last_activity (активный клиент), score (баллы кредитного скоринга), gender (пол) и age (возраст). А самая большая прямая зависимость со столбцом products (количество продуктов, которыми пользуется клиент).
Построим столбец корриляций:
sns.heatmap(
bank_scrooge_corr.loc[:, ['churn']].drop('churn').sort_values('churn'),
annot = True, fmt = '.0%', cmap = 'flag', center = 0);
По уменьшению корриляции для столбца churn идут столбцы: products, equity, last_activity, score, gender, credit_card, age, estimated_salary, balance, city.
Построим матрицу корреляций:
Построим тепловую карту, вычесленных корриляций (корреляции Пирсена):
sns.heatmap(bank_scrooge_corr, annot = True, square = True)
plt.title('Матрица корреляций', fontsize = 30)
sns.set(rc = {'figure.figsize': (15, 15)})
plt.yticks(fontsize = 15)
plt.xticks(fontsize = 15)
plt.show()
Вывод: наибольшая корреляция между churn (ушел клиент или нет) и столбцом products (количество продуктов, которыми пользуется клиент). Т.е. чем большими продуктами пользуется клиент, тем менее вероятно, что он уйдет. Также присутствует корреляция для столбца equity (количество баллов собственности). А также для столбца last_activity (активный ли клиент).
По возрасту age:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'age',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Исходя из построенной нормированной гистограммы, получаются категории:
Наибольший отток клиентов происходят в возрасте от 25 до 35, а также от 50 до 60 лет есть тоже значительный отток.
Создадим новый столбец age_category, где в зависимости от возраста клиента, укажем его принадлежность к определенному возрасту:
bank_scrooge.loc[(bank_scrooge['age'] < 25), 'age_category'] = 'young'
bank_scrooge.loc[(bank_scrooge['age'] >= 25) & (bank_scrooge['age'] <= 35), 'age_category'] = 'mature'
bank_scrooge.loc[(bank_scrooge['age'] > 35) & (bank_scrooge['age'] < 50), 'age_category'] = 'middle age'
bank_scrooge.loc[(bank_scrooge['age'] >= 50) & (bank_scrooge['age'] <= 60), 'age_category'] = 'old'
bank_scrooge.loc[(bank_scrooge['age'] > 60), 'age_category'] = 'pensioner'
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов по созданным категориям возраста:
table_age = bank_scrooge.pivot_table(index = 'churn', columns = 'age_category', values = 'age', aggfunc = 'count')
display(table_age)
labels = ['оставшиеся', 'ушедшие']
| age_category | mature | middle age | old | pensioner | young |
|---|---|---|---|---|---|
| churn | |||||
| 0 | 2390 | 3294 | 1300 | 966 | 205 |
| 1 | 665 | 588 | 468 | 67 | 31 |
Построим круговую диаграмму для того чтобы определить какая возрастная категория более склонна к оттоку:
table_age = table_age.reset_index()
fig = make_subplots(rows = 1, cols = 5, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_age[table_age.columns[1]],
labels = labels,
title = 'mature(25-35)'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_age[table_age.columns[2]],
labels = labels,
title = 'middle age(35-50)'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_age[table_age.columns[3]],
labels = labels,
title = 'old(50-60)'),
row = 1, col = 3)
fig.add_trace(go.Pie(
values = table_age[table_age.columns[4]],
labels = labels,
title = 'pensioner(60+)'),
row = 1, col = 4)
fig.add_trace(go.Pie(
values = table_age[table_age.columns[5]],
labels = labels,
title = 'young(18-25)'),
row = 1, col = 5)
fig.show()
Вывод: клиенты в возрасте от 50 до 60 лет включительно, более всего склонны к оттоку, относительно других групп, целых 26.5% от всего количества. Далее идет группа возраста от 25 до 35 лет включительно, в ней отток равен 21.8%. Далее идет возрастная категория от 35 до 50, у нее процент оттока равен 15.1. Далее группа молодых от 18 до 25 лет, с оттоком 13.1%. И наименьший процент оттока у категории пенсионеров 60+ у нее отток равен всего 6.49 процента.
По полу gender:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'gender',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от их пола:
table_gender = bank_scrooge.pivot_table(index = 'churn', columns = 'gender', values = 'age', aggfunc = 'count')
display(table_gender)
| gender | 0 | 1 |
|---|---|---|
| churn | ||
| 0 | 4347 | 3808 |
| 1 | 636 | 1183 |
Построим круговую диаграмму для того чтобы определить какая пол более склонен к оттоку:
table_gender = table_gender.reset_index()
fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_gender[table_gender.columns[1]],
labels = labels,
title = 'Женщины'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_gender[table_gender.columns[2]],
labels = labels,
title = 'Мужчины'),
row = 1, col = 2)
fig.show()
Вывод: можно заметить, что клиенты мужского пола более склонны к оттоку, нежели женщины. У мужчин 23.7% оттока от всего количества, когда у женщин всего 12.8%.
По количеству баллов собственности equity:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'equity',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от количества их баллов собственности:
table_equity = bank_scrooge.pivot_table(index = 'churn', columns = 'equity', values = 'age', aggfunc = 'count')
display(table_equity)
| equity | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
| churn | ||||||||||
| 0 | 2486 | 680 | 885 | 1219 | 1383 | 1339 | 103 | 43 | 11 | 6 |
| 1 | 90 | 93 | 166 | 322 | 464 | 576 | 58 | 37 | 6 | 7 |
Построим круговую диаграмму для того чтобы определить как влияет количества баллов собственности клиента на их отток (построим наглядный график для клиентов, у которых баллов не более 5, так как большее количество баллов у очень маленького количества клиентов):
fig = make_subplots(rows = 1, cols = 6, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[1]],
labels = labels,
title = '0 баллов'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[2]],
labels = labels,
title = '1 балл'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[3]],
labels = labels,
title = '2 балла'),
row = 1, col = 3)
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[4]],
labels = labels,
title = '3 балла'),
row = 1, col = 4)
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[5]],
labels = labels,
title = '4 балла'),
row = 1, col = 5)
fig.add_trace(go.Pie(
values = table_equity[table_equity.columns[6]],
labels = labels,
title = '5 баллов'),
row = 1, col = 6)
fig.show()
Вывод: чем больше у клиентов баллов собственности, тем более вероятен отток клиента. Например, процент оттока клиентов, у кого 0 баллов равен 3.49% от всего количества. А вот у клиентов, у которых 5 баллов уже 30.1% оттока. И с каждым следующим баллов сохраняется тенденция увеличения доли оттока клиентов.
По количеству продуктов, которыми пользуется клиент products:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'products',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от количества продуктов, которыми пользуется клиент:
table_products = bank_scrooge.pivot_table(index = 'churn', columns = 'products', values = 'age', aggfunc = 'count')
display(table_products)
| products | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| churn | ||||||
| 0 | NaN | 3088.0 | 4141.0 | 741.0 | 174.0 | 11.0 |
| 1 | 1.0 | 235.0 | 978.0 | 297.0 | 300.0 | 8.0 |
Построим круговую диаграмму для того чтобы определить как влияет количеству продуктов, которыми пользуется клиент на их отток (круговую диаграмму для клиентов, у которых 0 или 5 продуктов, можно не рассматривать, так как таких клиентов крайне мало):
table_products = table_products.reset_index()
fig = make_subplots(rows = 1, cols = 4, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_products[table_products.columns[2]],
labels = labels,
title = '1 продукт'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_products[table_products.columns[3]],
labels = labels,
title = '2 продукта'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_products[table_products.columns[4]],
labels = labels,
title = '3 продукта'),
row = 1, col = 3)
fig.add_trace(go.Pie(
values = table_products[table_products.columns[5]],
labels = labels,
title = '4 продукта'),
row = 1, col = 4)
fig.show()
Вывод: заметна тенденция, что чем больше продуктов, которыми пользуется клиент, тем больше доля оттока таких клиентов. Например, клиент, у которого один продукт процент оттока таких клиентов равен 7, от всего количества. А вот для клиентов, у которых 4 продукта процент оттока уже равен 63.3%. Что довольно логично.
Есть ли кредитная карта credit_card:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'credit_card',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от наличия кредитной карты:
table_credit_card = bank_scrooge.pivot_table(index = 'churn', columns = 'credit_card', values = 'age', aggfunc = 'count')
display(table_credit_card)
| credit_card | 0 | 1 |
|---|---|---|
| churn | ||
| 0 | 2374 | 5781 |
| 1 | 816 | 1003 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от наличия кредитной карты:
table_credit_card = table_credit_card.reset_index()
fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_credit_card[table_credit_card.columns[1]],
labels = labels,
title = 'Нет кредитной карты'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_credit_card[table_credit_card.columns[2]],
labels = labels,
title = 'Есть кредитная карта'),
row = 1, col = 2)
fig.show()
Вывод: больше отток у клиентов, у которых нет кредитной карты, он равен почти 26% от всего количества, для клиентов у которых есть кредитная карта, он равен всего 15%.
Рекомендация: необходимо проводить акции для клиентов, например, предлагать кредитные карты с бесплатным обслуживанием в течение года, при оформлении. Так как статистика показывает, что для клиентов, у которых оформлена кредитная карта, им менее свойственен отток.
По активности клиента last_activity:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'last_activity',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от активности клиента:
table_activity = bank_scrooge.pivot_table(index = 'churn', columns = 'last_activity', values = 'age', aggfunc = 'count')
display(table_activity)
| last_activity | 0 | 1 |
|---|---|---|
| churn | ||
| 0 | 4217 | 3938 |
| 1 | 542 | 1277 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от активности клиента:
table_activity = table_activity.reset_index()
fig = make_subplots(rows = 1, cols = 2, specs = [[{"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_activity[table_activity.columns[1]],
labels = labels,
title = 'Активный клиент'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_activity[table_activity.columns[2]],
labels = labels,
title = 'Не активный клиент'),
row = 1, col = 2)
fig.show()
Вывод: что и следовало ожидать, что у активных клиентов процент оттока меньше. У активных клиентов процент оттока равен 11.4% от всего количества. А вот у не активных клиентов он равен почти 25%.
Рекомендации: необходимо анализировать последнюю активность клиента, и если последняя активность была более, например, месяца назад. То необходимо стараться завлечь клиента пользованием картой. К примеру, некими акциями или кэшбэком, или же беспроцентными переводами в течение месяца.
По баллам кредитного скоринга score:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'score',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Наибольший отток клиентов происходит в диапазоне баллов кредитного скорринга от 825 до 910 (включительно), а также довольно ощутимый отток происходит от 930 до до 940 (включительно). Выделим основные группы:
Создадим новый столбец score_category, где в зависимости от баллов кредитного скоринга, укажем принадлежность клиента к определенной категории:
bank_scrooge.loc[(bank_scrooge['score'] < 825), 'score_category'] = '-825'
bank_scrooge.loc[(bank_scrooge['score'] >= 825) & (bank_scrooge['score'] <= 910), 'score_category'] = '825-910'
bank_scrooge.loc[(bank_scrooge['score'] > 910) & (bank_scrooge['score'] < 930), 'score_category'] = '910-930'
bank_scrooge.loc[(bank_scrooge['score'] >= 930) & (bank_scrooge['score'] <= 940), 'score_category'] = '930-940'
bank_scrooge.loc[(bank_scrooge['score'] > 940), 'score_category'] = '940+'
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от баллов кредитного скоринга:
table_score = bank_scrooge.pivot_table(index = 'churn', columns = 'score_category', values = 'score', aggfunc = 'count')
display(table_score)
| score_category | -825 | 825-910 | 910-930 | 930-940 | 940+ |
|---|---|---|---|---|---|
| churn | |||||
| 0 | 3199 | 3418 | 757 | 244 | 537 |
| 1 | 409 | 1095 | 165 | 65 | 85 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от баллов кредитного скоринга:
table_score = table_score.reset_index()
fig = make_subplots(rows = 1, cols = 5, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_score[table_score.columns[1]],
labels = labels,
title = 'Менее 825 баллов кредитного скоринга'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_score[table_score.columns[2]],
labels = labels,
title = 'От 825 до 910 баллов кредитного скоринга'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_score[table_score.columns[3]],
labels = labels,
title = 'От 910 до 930 баллов кредитного скоринга'),
row = 1, col = 3)
fig.add_trace(go.Pie(
values = table_score[table_score.columns[4]],
labels = labels,
title = 'От 930 до 940 баллов кредитного скоринга'),
row = 1, col = 4)
fig.add_trace(go.Pie(
values = table_score[table_score.columns[5]],
labels = labels,
title = 'Свыше 940 баллов кредитного скоринга'),
row = 1, col = 5)
fig.show()
Вывод: можно увидеть, что наибольший процентный отток клиентов, у которых среднее количество баллов кредитного скоринга (от 825 до 910) он равен 24.3% от всего количества. Далее идет категория, у которой много баллов кредитного скоринга (от 940 до 940) процент оттока клиентов данной категории равен почти 21%. И самый малый процент оттока клиентов, у которых мало баллов кредитного скоринга (до 825) их процент оттока равен 11.3.
По городу city:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'city',
stat = 'density',
common_norm = False,
palette = 'flag',
bins = 3
);
Клиентам из городов: Ярославль и Ростов более свойственен отток, нежели клиенты, который проживают в Рыбинске.
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от города:
table_city = bank_scrooge.pivot_table(index = 'churn', columns = 'city', values = 'age', aggfunc = 'count')
display(table_city)
| city | Ростов | Рыбинск | Ярославль |
|---|---|---|---|
| churn | |||
| 0 | 1151 | 2246 | 4758 |
| 1 | 265 | 437 | 1117 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от города:
table_city = table_city.reset_index()
fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_city[table_city.columns[1]],
labels = labels,
title = 'Ростов'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_city[table_city.columns[2]],
labels = labels,
title = 'Рыбинск'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_city[table_city.columns[3]],
labels = labels,
title = 'Ярославль'),
row = 1, col = 3)
fig.show()
Вывод: почти одинаковых процент оттока клиентов из города Ростов и Ярославль он равен 18.7 и 19%. А вот для клиентов из города Рыбинска он равен 16.3% от всего количества.
По балансу на счете balance:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge.query('balance != -1'),
hue = 'churn',
x = 'balance',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Видим выбросы все, что выше 5000000, сделаем срез данных, чтобы получить наглядные результаты.
plt.figure(figsize = (15, 10))
plt.xlim([0, 5000000])
sns.histplot(
data = bank_scrooge.query('balance != -1'),
hue = 'churn',
x = 'balance',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Исходя из построенной нормированной гистограммы, получаются три категории:
И большой отток среди тех клиентов, у которых баланс свыше 1000000 рублей.
Создадим новый столбец balance_category, где в зависимости от баланса клиента на счёте, укажем его принадлежность к определенной категории (low_balance - низкий баланс, middle_balance - средний баланс, high_balance - высокий баланс):
bank_scrooge.loc[(bank_scrooge['balance'] <= 800000), 'balance_category'] = 'low_balance'
bank_scrooge.loc[(bank_scrooge['balance'] > 800000) & (bank_scrooge['balance'] < 4200000), 'balance_category'] = 'middle_balance'
bank_scrooge.loc[(bank_scrooge['balance'] >= 4200000), 'balance_category'] = 'high_balance'
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от баланса:
table_balance = bank_scrooge.pivot_table(index = 'churn', columns = 'balance_category', values = 'balance', aggfunc = 'count')
display(table_balance)
| balance_category | high_balance | low_balance | middle_balance |
|---|---|---|---|
| churn | |||
| 0 | 70 | 4270 | 1549 |
| 1 | 36 | 918 | 852 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в баланса:
table_balance = table_balance.reset_index()
fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_balance[table_balance.columns[1]],
labels = labels,
title = 'Большой баланс'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_balance[table_balance.columns[2]],
labels = labels,
title = 'Низкий баланс'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_balance[table_balance.columns[3]],
labels = labels,
title = 'Средний баланс'),
row = 1, col = 3)
fig.show()
Вывод: наибольший отток клиентов, у которых баланс на счёте находится в диапазоне от 800000 до 4200000 рублей, для них процент оттока равен 35.5 от всего количества. Далее идут клиенты из категории большого баланса (свыше 4200000 рублей на счету) для них процент оттока равен 34. Ну и самый маленький процент оттока у клиентов с низким балансом, он равен 12.5.
По заработной плате estimated_salary:
plt.figure(figsize = (15, 10))
sns.histplot(
data = bank_scrooge,
hue = 'churn',
x = 'estimated_salary',
stat = 'density',
common_norm = False,
palette = 'flag'
);
Исходя из построенной нормированной гистограммы, получаются три категории:
Отток клиентов с зарплатой ниже 80 тыс. рублей не большой, а вот для клиентов с более высокой зарплатой отток становится больше. А наибольший отток клиентов происходит в диапозоне от 80 тыс. до 300 тыс. рублей заработной платы.
Создадим новый столбец salary_category, где в зависимости от заработной платы клиента, укажем его принадлежность к определенной категории (low_salary - низкая заработная плата, middle_salary - средняя заработная плата, high_salary - высокая заработная плата):
bank_scrooge.loc[(bank_scrooge['estimated_salary'] < 80000), 'salary_category'] = 'low_salary'
bank_scrooge.loc[(bank_scrooge['estimated_salary'] >= 80000) & (bank_scrooge['estimated_salary'] <= 300000), 'salary_category'] = 'middle_salary'
bank_scrooge.loc[(bank_scrooge['estimated_salary'] > 300000), 'salary_category'] = 'high_salary'
Создадим сводную таблицу, где отобразим количество ушедших и оставшихся клиентов в зависимости от заработной платы:
table_salary = bank_scrooge.pivot_table(index = 'churn', columns = 'salary_category', values = 'estimated_salary', aggfunc = 'count')
display(table_salary)
| salary_category | high_salary | low_salary | middle_salary |
|---|---|---|---|
| churn | |||
| 0 | 467 | 2344 | 5344 |
| 1 | 95 | 418 | 1306 |
Построим круговую диаграмму для того чтобы наглядно отобразить процент оттока клиентов в зависимости от заработной платы клиента:
table_salary = table_salary.reset_index()
fig = make_subplots(rows = 1, cols = 3, specs = [[{"type": "pie"}, {"type": "pie"}, {"type": "pie"}]])
fig.add_trace(go.Pie(
values = table_salary[table_salary.columns[1]],
labels = labels,
title = 'Высокая ЗП'),
row = 1, col = 1)
fig.add_trace(go.Pie(
values = table_salary[table_salary.columns[2]],
labels = labels,
title = 'Низкая ЗП'),
row = 1, col = 2)
fig.add_trace(go.Pie(
values = table_salary[table_salary.columns[3]],
labels = labels,
title = 'Средняя ЗП'),
row = 1, col = 3)
fig.show()
Вывод: наибольший процент оттока у клиентов, принадлежащих к категории средней ЗП (80 тыс. до 300 тыс. рублей) он равен почти 20 от всего количества. Далее идет категория клиентов с высокой ЗП (свыше 300 тыс. рублей) для нее процент оттока равен 16.9. Ну и самый маленький процент оттока клиентов с низкой ЗП (до 80 тыс. рублей) он равен почти 15.
Проверка гипотезы различия дохода между теми клиентами, которые ушли и теми, которые остались:
Сформулируем гипотезы:
Нулевая гипотеза будет гласить, что различия в зарплате между теми клиентами, которые ушли и теми, которые остались, не значительные.
Альтернативная гипотеза будет гласить, что существуют различия в зарплате между теми клиентами, которые ушли и теми, которые остались.
p-value (уровень значимости) возьмем равный 0.05.
results = st.ttest_ind(
bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'],
bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'],
equal_var = False)
alpha = 0.05 # значение уровня значимости
print('p-значение:', results.pvalue)
if results.pvalue < alpha:
print(results.pvalue, "Отвергаем нулевую гипотезу")
else:
print(results.pvalue, "Не получилось отвергнуть нулевую гипотезу")
p-значение: 0.8515828926028186 0.8515828926028186 Не получилось отвергнуть нулевую гипотезу
# определим статистическую значимость между доходами в двух группах:
print('{0:.4f}'.format(stats.mannwhitneyu(bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'],
bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'])[1]))
# определим отншение среднего дохода клиентов, которые ушли, к среднему доходу клиентов, которые остались:
print('{0:.4f}'.format(bank_scrooge[bank_scrooge['churn'] == 0]['estimated_salary'].mean() / bank_scrooge[bank_scrooge['churn'] == 1]['estimated_salary'].mean()))
0.0002 0.9959
Вывод по полученным результатам:
p-value значительно больше 0.05. И равно 0.85, это значит, что не существует значимых статистических различий между зарплатами клиентов, которые ушли и теми, которые остались, поэтому принимаем нулевую гипотезу, и отвергаем альтернативную
Получается, что ЗП, у клиентов, которые остались, в среднем в 1.003 раза меньше тех, кто ушел.
Причина массового оттока клиентов заключается не в низком уровне зарплаты
Посчитаем статистическую значимость различий в значении кредитного скоринга между теми клиентами, которые ушли и теми, которые остались:
Сформулируем гипотезы:
Нулевая гипотеза будет гласить, что различие в значениях кредитного скоринга между теми клиентами, которые ушли и теми, которые остались, не значительное.
Альтернативная гипотеза будет гласить, что существует различие в значениях кредитного скоринга клиентов, которые ушли и теми, которые остались.
p-value (уровень значимости) возьмем равный 0.05.
results = st.ttest_ind(
bank_scrooge[bank_scrooge['churn'] == 0]['score'],
bank_scrooge[bank_scrooge['churn'] == 1]['score'],
equal_var = False)
alpha = 0.05 # значение уровня значимости
print('p-значение:', results.pvalue)
if results.pvalue < alpha:
print(results.pvalue, "Отвергаем нулевую гипотезу")
else:
print(results.pvalue, "Не получилось отвергнуть нулевую гипотезу")
p-значение: 8.60332714629934e-38 8.60332714629934e-38 Отвергаем нулевую гипотезу
print('{0:.4f}'.format(stats.mannwhitneyu(bank_scrooge[bank_scrooge['churn'] == 0]['score'],
bank_scrooge[bank_scrooge['churn'] == 1]['score'])[1]))
print('{0:.4f}'.format(bank_scrooge[bank_scrooge['churn'] == 0]['score'].mean() / bank_scrooge[bank_scrooge['churn'] == 1]['score'].mean()))
0.0000 0.9791
Вывод по полученным результатам:
p-value меньше 0.05. Значит существуют весомые статистические различия между значениями кредитного скоринга клиентов, которые ушли и теми, которые остались, поэтому принимаем альтернативную гипотезу, и отвергаем нулевую гипотезу.
Выходит, что кредитный скоринг клиентов, которые остались, в среднем в 1.02 раза выше тех, кто ушел, что как показали результаты является значимым расхождением. Причина массового оттока клиентов заключается в менее выгодных условиях выплате кредитов. Скорее всего, что клиенты, которые ушли из банка решили рефинансировать свой кредит в другом банке. Поэтому необходимо смягчить условия кредитования, а также, возможно, предоставлять кредитные каникулы некоторым клиентам, подходящие по портреты, описанные ранее.
Выделение высокооточных сегментов:
Сначала посмотрим на процент оттока по всем клиентам:
print(f"Процент оттока по всем клиентам банка равен: {round(((len(bank_scrooge.query('churn == 1')) / len(bank_scrooge)) * 100), 1)}")
Процент оттока по всем клиентам банка равен: 18.2
Выделим первый сигмент:
segment_1 = bank_scrooge.query("gender == 1 and city == 'Ярославль' and last_activity == 1")
print(f"Процент оттока клиентов для первого сегмента равен: {round(((len(segment_1.query('churn == 1')) / len(segment_1)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_1)}")
print(f"Всего клиентов которые ушли {len(segment_1.query('churn == 1'))}")
Процент оттока клиентов для первого сегмента равен: 29.9 Всего клиентов в данном сегменте: 1490 Всего клиентов которые ушли 445
Выделим второй сигмент:
segment_2 = bank_scrooge.query("age_category == 'old' and balance_category == 'middle_balance'")
print(f"Процент оттока клиентов для второго сегмента равен: {round(((len(segment_2.query('churn == 1')) / len(segment_2)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_2)}")
print(f"Всего клиентов которые ушли {len(segment_2.query('churn == 1'))}")
Процент оттока клиентов для второго сегмента равен: 45.5 Всего клиентов в данном сегменте: 550 Всего клиентов которые ушли 250
Выделим третий сигмент:
segment_3 = bank_scrooge.query("products >= 2 and credit_card == 0 and equity >= 5")
print(f"Процент оттока клиентов для второго сегмента равен: {round(((len(segment_3.query('churn == 1')) / len(segment_3)) * 100), 1)}")
print(f"Всего клиентов в данном сегменте: {len(segment_3)}")
print(f"Всего клиентов которые ушли {len(segment_3.query('churn == 1'))}")
Процент оттока клиентов для второго сегмента равен: 39.3 Всего клиентов в данном сегменте: 880 Всего клиентов которые ушли 346